#include "StdAfx.h"
#include "RibbonInfo.h"
#include <atlbase.h>
#include "xmllite.h"
#include <string>
#include <regex>
#include <algorithm>
#include <sstream>
#include <functional>

using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders; 

CRibbonInfo::CRibbonInfo(const CRibbonCompiler& ribbonCompiler) : m_ribbonCompiler(ribbonCompiler), m_dwApplicationModeBitmap(0) 
{
	ExtractInformationFromXML();
	ExtractInformationFromHeaderFile();
	sort(m_commandNames.begin(), m_commandNames.end());
	PopulateContextMapIds();
	PopulateContextMapTabIds();
	ConvertApplicationModeBitmapToVector();

	
}


void CRibbonInfo::ExtractInformationFromXML()
{
HRESULT hr;
    CComPtr<IStream> pFileStream;
    CComPtr<IXmlReader> pReader;
    XmlNodeType nodeType;
    const WCHAR* pwszPrefix;
    const WCHAR* pwszLocalName;
    const WCHAR* pwszValue;
    UINT cwchPrefix;

	bool bInContextualTabTree = false;

	if (FAILED(hr = SHCreateStreamOnFile(m_ribbonCompiler.GetSourceXMLFilePath().c_str(), STGM_READ, &pFileStream)))
    {
		//Change to real exception with the hr
        throw L"Error creating file reader";
    }

    if (FAILED(hr = CreateXmlReader(__uuidof(IXmlReader), (void**) &pReader, NULL)))
    {
		//Change to real exception with the hr
        throw "Error creating xml reader";
    }

    if (FAILED(hr = pReader->SetProperty(XmlReaderProperty_DtdProcessing, DtdProcessing_Prohibit)))
    {
		//Change to real exception with the hr
        throw L"Error setting XmlReaderProperty_DtdProcessing";
    }

    if (FAILED(hr = pReader->SetInput(pFileStream)))
    {
		//Change to real exception with the hr
       throw L"Error setting input for reader";
    }

    //read until there are no more nodes
    while (S_OK == (hr = pReader->Read(&nodeType)))
    {
        switch (nodeType)
        {
			//TODO: search for Command.Name property element also
     
        case XmlNodeType_Element:
				{
				if (FAILED(hr = pReader->GetPrefix(&pwszPrefix, &cwchPrefix)))
				{
					throw L"Error getting prefix";
				}
				if (FAILED(hr = pReader->GetLocalName(&pwszLocalName, NULL)))
				{
					throw "Error getting local name";
				}
				wstring elementName;

				if (cwchPrefix > 0)
				{
					elementName.append(pwszPrefix);
					elementName.append(L":");
				}
				elementName.append(pwszLocalName);
				hr = pReader->MoveToAttributeByName(L"ApplicationModes", NULL);
				if (hr == S_OK)
				{
					hr = pReader->GetValue(&pwszValue, &cwchPrefix);
					if (hr == S_OK)
						PopulateApplicationMode(pwszValue);
				}

				if (elementName == L"Command")
				{
					hr = pReader->MoveToAttributeByName(L"Name", NULL);
					if (FAILED(hr))
						break;
					hr = pReader->GetValue(&pwszValue, &cwchPrefix);
					if (FAILED(hr))
						break;
					m_commandNames.push_back(pwszValue);
				}
				else if (elementName == L"ContextMap")
				{
					hr = pReader->MoveToAttributeByName(L"CommandName", NULL);
					if (FAILED(hr))
						break;
					hr = pReader->GetValue(&pwszValue, &cwchPrefix);
					if (FAILED(hr))
						break;
					m_ContextMapCommandNames.push_back(pwszValue);
				}
				else if (elementName == L"Ribbon.ContextualTabs")
				{
					bInContextualTabTree = true;
				}
				else if (bInContextualTabTree && (elementName == L"TabGroup"))
				{
					hr = pReader->MoveToAttributeByName(L"CommandName", NULL);
					if (FAILED(hr))
						break;
					hr = pReader->GetValue(&pwszValue, &cwchPrefix);
					if (FAILED(hr))
						break;
					m_ContextTabMapCommandNames.push_back(pwszValue);
				
				}
			}
			break;
               
		case XmlNodeType_EndElement:
			{
				if (FAILED(hr = pReader->GetPrefix(&pwszPrefix, &cwchPrefix)))
				{
					throw L"Error getting prefix";
				}
				if (FAILED(hr = pReader->GetLocalName(&pwszLocalName, NULL)))
				{
					throw "Error getting local name";
				}
				wstring elementName;

				if (cwchPrefix > 0)
				{
					elementName.append(pwszPrefix);
					elementName.append(L":");
				}
				elementName.append(pwszLocalName);
				if (elementName == L"Ribbon.ContextualTabs")
				{
					bInContextualTabTree = false;
				}			
			}
            
			//TODO: search for Command.Name property element text
        case XmlNodeType_Text:
        case XmlNodeType_Whitespace:
            if (FAILED(hr = pReader->GetValue(&pwszValue, NULL)))
            {
                throw L"Error getting value";
            }
            break;
        }
    }
}

void CRibbonInfo::PopulateApplicationMode(PCWSTR mode)
{
	basic_istringstream<wchar_t> modeString(mode);
	unsigned int val; 
	wchar_t c;

	while (modeString)
	{
		modeString >> val;
		if (val)
		{
			m_dwApplicationModeBitmap |= (1 << (val - 1));
		}
		
		//consume whitespace and ,
		//while (modeString && !iswdigit(modeString.peek()))
		if (modeString)
			modeString >> c;
	}

}

void CRibbonInfo::ExtractInformationFromHeaderFile()
{
	wstring headerFileText = m_ribbonCompiler.GetHeaderFileText(); 
	
	wistringstream source(headerFileText);
	wstring line;
	
	while (getline(source, line))
	{
		wstring::size_type place = line.find(L"#define ");
		if (place == 0)
		{
			wstring command;
			int id;
			wistringstream sourceLine(line.substr(8));
			sourceLine >> command;
			sourceLine >> id;
			m_commandsName2IdMap[command] = id;
			m_commandsId2NameMap[id] = command;
		}
	}
}


CRibbonInfo::~CRibbonInfo(void)
{
}


void CRibbonInfo::PopulateContextMapIds()
{
	for_each(m_ContextMapCommandNames.begin(), m_ContextMapCommandNames.end(),tr1::bind(&CRibbonInfo::PopulateContextMapId, this, _1));
}

void CRibbonInfo::PopulateContextMapId(const std::wstring &contextMapCommandName)
{
	m_commandIdsForContextPopup.push_back(m_commandsName2IdMap[contextMapCommandName]);
}

void CRibbonInfo::PopulateContextMapTabIds()
{
	for_each(m_ContextTabMapCommandNames.begin(), m_ContextTabMapCommandNames.end(),tr1::bind(&CRibbonInfo::PopulateContextMapTabId, this, _1));
}

void CRibbonInfo::PopulateContextMapTabId(const std::wstring &contextMapTabCommandName)
{
	m_commandIdsForContextTab.push_back(m_commandsName2IdMap[contextMapTabCommandName]);
}

void CRibbonInfo::ConvertApplicationModeBitmapToVector()
{
	m_applicationModes.push_back(0);
	unsigned int appModes = m_dwApplicationModeBitmap;

	for (int i = 1; i < 32; ++i)
	{
		if (appModes & 1)
			m_applicationModes.push_back(i);
		appModes >>= 1;
	}
}